package ua.yandex.collections;

public class MyArrayList implements MyList {

    private static final int MINIMAL_LENGTH = 10;
    private static final int INCREASE_FACTOR = 3;

    private Object[] array;
    private int size;

    public MyArrayList() {
        array = new Object[MINIMAL_LENGTH];
        size = 0;
    }

    public MyArrayList(Object[] c) {
        array = c.clone();
        size = c.length;
    }

    public MyArrayList(int capacity) {
        if (capacity < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        array = new Object[capacity];
        size = 0;
    }

    public int getCapacity() {
        return array.length;
    }

    public void ensureCapacity(int minCapacity) {
        if (minCapacity < size) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (array.length < minCapacity) {
            Object[] newArray = new Object[minCapacity];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }
    }

    @Override
    public void add(Object e) {
        if (size == array.length) {
            Object[] newArray = new Object[(size * INCREASE_FACTOR) / 2 + 1];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }
        array[size] = e;
        size++;
    }

    @Override
    public void add(int index, Object e) {
        if (index > size || index < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (size == array.length) {
            Object[] newArray = new Object[(size * INCREASE_FACTOR) / 2 + 1];
            System.arraycopy(array, 0, newArray, 0, index);
            newArray[index] = e;
            System.arraycopy(array, index, newArray, index + 1, size - index);
            array = newArray;
        } else {
            System.arraycopy(array, index, array, index + 1, size - index);
            array[index] = e;
        }
        size++;
    }

    @Override
    public void addAll(Object[] c) {
        if (array.length - size < c.length) {
            Object[] newArray = new Object[array.length + c.length];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }
        System.arraycopy(c, 0, array, size, c.length);
        size += c.length;
    }

    @Override
    public void addAll(int index, Object[] c) {
        if (index > size || index < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (array.length - size < c.length) {
            Object[] newArray = new Object[array.length + c.length];
            System.arraycopy(array, 0, newArray, 0, index);
            System.arraycopy(c, 0, newArray, index, c.length);
            System.arraycopy(array,
                    index,
                    newArray,
                    index + c.length,
                    size - index);
            array = newArray;
        } else {
            System.arraycopy(array,
                    index,
                    array,
                    index + c.length,
                    size - index);
            System.arraycopy(c, 0, array, index, c.length);
        }
        size += c.length;
    }

    @Override
    public Object get(int index) {
        if (index >= size || index < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return array[index];
    }

    @Override
    public Object remove(int index) {
        if (index >= size || index < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        Object removed = array[index];
        size--;
        if ((size * INCREASE_FACTOR) / 2 < array.length) {
            Object[] newArray = new Object[(size * INCREASE_FACTOR) / 2 + 1];
            System.arraycopy(array, 0, newArray, 0, index);
            System.arraycopy(array, index + 1, newArray, index, size - index);
            array = newArray;
        } else {
            System.arraycopy(array, index + 1, array, index, size - index);
        }
        return removed;
    }

    @Override
    public void set(int index, Object e) {
        if (index >= size || index < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        array[index] = e;
    }

    @Override
    public int indexOf(Object o) {
        int counter = 0;
        boolean b = true;
        while (b && counter < size) {
            if (array[counter].equals(o)) {
                b = false;
            } else {
                counter++;
            }
        }
        if (b) {
            return -1;
        } else {
            return counter;
        }
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        array = new Object[MINIMAL_LENGTH];
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Object[] toArray() {
        Object[] rez = new Object[size];
        System.arraycopy(array, 0, rez, 0, size);
        return rez;
    }

    @Override
    public String toString() {
        if (size == 0) {
            return "";
        } else {
            StringBuilder result = new StringBuilder(array[0].toString());
            for (int i = 1; i < size; i++) {
                result.append(",");
                result.append(array[i].toString());
            }
            return result.toString();
        }
    }

}
